package com.guguskins.back.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guguskins.back.common.constant.InventoryConstant;
import com.guguskins.back.common.constant.RecordConstant;
import com.guguskins.back.common.constant.RedisConstant;
import com.guguskins.back.common.exception.GlobalMsgException;
import com.guguskins.back.entity.*;
import com.guguskins.back.entity.bo.InventoryBo;
import com.guguskins.back.entity.vo.InventoryVo;
import com.guguskins.back.entity.vo.PageVo;
import com.guguskins.back.mapper.GuInventoryMapper;
import com.guguskins.back.service.*;
import com.guguskins.back.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author mybatis-plus
 * @since 2021-05-18
 */
@Service
public class GuInventoryServiceImpl extends ServiceImpl<GuInventoryMapper, GuInventory> implements GuInventoryService {

    @Autowired
    private GuInventoryMapper inventoryMapper;

    @Autowired
    private GuGoodsService goodsService;
    @Autowired
    private GuPropService propService;
    @Autowired
    private GuContractRateService contractRateService;

    @Autowired
    private GuUserService userService;

    @Autowired
    private GuRecordService recordService;

    @Autowired
    private GuBoxService boxService;

    @Autowired
    private GuStockService stockService;

    @Autowired
    private GuWithdrawalService withdrawalService;

    @Autowired
    private GuCurrencyRecordService currencyRecordService;

    @Override
    public boolean updateRecordStateByIds(List<Long> ids, Integer state) {
        List<GuInventory> inventories = this.listByIds(ids);
        inventories.forEach(record -> {
            record.setInventoryState(state);
        });
        return this.updateBatchById(inventories);
    }

    @Override
    public GuInventory addInventory(Long userId, String steamId, int type, int source, Long sourceId, Long itemId, BigDecimal itemPrice) {
        GuInventory inventory = new GuInventory();
        inventory.setInventoryUserId(userId);
        if (ObjectUtils.isEmpty(steamId)) {
            GuUser user = userService.getById(userId);
            steamId = user.getUserSteamId();
        }
        inventory.setInventorySteamId(steamId);
        inventory.setInventoryType(type);
        inventory.setInventorySource(source);
        inventory.setInventorySourceId(sourceId);
        inventory.setInventoryItemId(itemId);
        switch (inventory.getInventoryType()) {
            case InventoryConstant.Type.BOX:
                GuBox box = boxService.getBox(inventory.getInventoryItemId());
                inventory.setInventoryItemName(box.getBoxName());
                break;
            case InventoryConstant.Type.GOODS:
                GuGoods goods = goodsService.getById(inventory.getInventoryItemId());
                inventory.setInventoryItemName(goods.getGoodsName());
                break;
            default:
                break;
        }
        //设置价格
        if (!ObjectUtils.isEmpty(itemPrice)) {
            inventory.setInventoryItemPrice(itemPrice);
        }
        inventory.setInventoryState(InventoryConstant.State.INVENTORY);
        if (inventory.insert()) {
            // 记录
//            recordService.addRecord();
            recordService.addRecord(userId, RecordConstant.TYPE.GAIN, source, sourceId, type, itemId, "获得饰品或箱子", "");
        }
        return inventory;
    }

    @Override
    public PageVo<InventoryVo> getInventory(Long userId, int state, int p, int size,int type) {
        PageVo<InventoryVo> pageVo = new PageVo<>();
        int index = (p - 1) * size;
        List<GuInventory> inventories = this.list(new LambdaQueryWrapper<GuInventory>() {
            {
                eq(GuInventory::getInventoryUserId, userId);
                eq(state > 0, GuInventory::getInventoryState, state);
                eq(GuInventory::getInventoryType,type);
                orderByAsc(GuInventory::getInventoryType);
                orderByAsc(GuInventory::getInventoryCreateTime);
                last("limit " + index + ", " + size);
            }
        });
        List<InventoryVo> inventoryVoList = new ArrayList<>();
        inventories.forEach(inventory -> {
            InventoryVo inventoryVo = new InventoryVo();
            if(inventory.getInventoryType()==2){
                inventoryVo.setLevel(goodsService.getById(inventory.getInventorySourceId()).getGoodsLevel());
            }
            inventoryVo.setInventoryId(inventory.getInventoryId());
            inventoryVo.setInventoryType(inventory.getInventoryType());
            inventoryVo.setInventoryState(inventory.getInventoryState());
            inventoryVo.setItemId(inventory.getInventoryItemId());
            switch (inventory.getInventoryType()) {
                case InventoryConstant.Type.BOX:
                    GuBox box = boxService.getBox(inventory.getInventoryItemId());
                    inventoryVo.setItemName(box.getBoxName());
                    inventoryVo.setItemImage(box.getBoxImageUrl());
                    inventoryVo.setItemSubImage(box.getGoodsList().stream().max(Comparator.comparing(GuGoods::getGoodsPrice)).orElse(new GuGoods()).getGoodsIconUrl());
                    inventoryVo.setLevel(5);
                    break;
                case InventoryConstant.Type.GOODS:
                    GuGoods goods = goodsService.getById(inventory.getInventoryItemId());
                    inventoryVo.setItemName(goods.getGoodsName());
                    inventoryVo.setItemImage(goods.getGoodsIconUrl());
                    inventoryVo.setItemPrice(goods.getGoodsPrice());
                    inventoryVo.setLevel(goods.getGoodsLevel());
                    break;
                default:
                    break;
            }
            inventoryVoList.add(inventoryVo);
        });
        pageVo.setData(inventoryVoList);
        long inventoryCount = this.count(new LambdaQueryWrapper<GuInventory>() {
            {
                eq(GuInventory::getInventoryUserId, userId);
                eq(state > 0, GuInventory::getInventoryState, state);
            }
        });
        pageVo.setTotal(inventoryCount);
        return pageVo;
    }

    @Override
    public PageVo<GuGoods> getGoodsInventory(Long userId, int state, int p, int size) {
        PageVo<GuGoods> pageVo = new PageVo<>();
        int index = (p - 1) * size;
        List<GuGoods> inventoryPageData = inventoryMapper.getGoodsInventoryPageData(userId, state, index, size);
        pageVo.setData(inventoryPageData);
        long inventoryCount = inventoryMapper.getGoodsInventoryCount(userId, state);
        pageVo.setTotal(inventoryCount);
        return pageVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GuGoods synthetic(Long userId, List<Long> inventoryIds) {
        GuUser user = userService.getById(userId);

        if (inventoryIds.size() < 3 || inventoryIds.size() > 10) {
            throw new GlobalMsgException("至少3件饰品");
        }

        List<GuInventory> inventories = this.listByIds(inventoryIds);
        if (inventories.size() != inventoryIds.size()
                || !inventories.stream().allMatch(
                inventory -> inventory.getInventoryUserId().equals(userId)
                        && inventory.getInventoryType().equals(InventoryConstant.Type.GOODS)
                        && inventory.getInventoryState().equals(InventoryConstant.State.INVENTORY))
        ) {
            throw new GlobalMsgException("参数异常");
        }

        //计算总价值A
        BigDecimal totalPrice = inventories.stream().map(GuInventory::getInventoryItemPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.err.println("提价材料总价值：" + totalPrice);
        //查询数据区间比例值
        GuContractRate contractRate = contractRateService.getById(1);

        //计算价值区间
        BigDecimal totalPriceMax = totalPrice.multiply(contractRate.getGoodsTotalMaxRate().divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN));
        BigDecimal totalPriceMin = totalPrice.multiply(contractRate.getGoodsTotalMinRate().divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN));
        System.err.println("总价值区间：" + totalPriceMin + " ~ " + totalPriceMax);

        //获取价值区间内所有饰品
        List<GuGoods> goodsList = goodsService.getGoodsList(-1L);
        //获取区间内道具
        List<GuGoods> intervalGoodsList = goodsList.stream().filter(guGoods -> guGoods.getGoodsPrice().compareTo(totalPriceMin) >= 0 && guGoods.getGoodsPrice().compareTo(totalPriceMax) <= 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(intervalGoodsList)) {
            //todo
            throw new GlobalMsgException("合成失败，请重新添加材料合成");
        }

        //计算平均值区间
        BigDecimal totalPriceMaxMinAvg = totalPriceMax.add(totalPriceMin).divide(new BigDecimal(2), 2, BigDecimal.ROUND_HALF_DOWN);
        System.err.println("价值区间平均值：" + totalPriceMaxMinAvg);
        BigDecimal priceAgvMax = totalPriceMaxMinAvg.multiply(contractRate.getGoodsAvgMaxRate().divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN));
        BigDecimal priceAgvMin = totalPriceMaxMinAvg.multiply(contractRate.getGoodsAvgMinRate().divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN));
        System.err.println("平均价值区间：" + priceAgvMin + " ~ " + priceAgvMax);

        //计算价值区间所有饰品平均值（更改-----可合成饰品需要单独建表）
        BigDecimal goodsPriceTotal = intervalGoodsList.stream().map(GuGoods::getGoodsPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.err.println("区间总价值：" + goodsPriceTotal);
        System.err.println("区间可合成数量：" + intervalGoodsList.size());
        BigDecimal goodsPriceAvg = goodsPriceTotal.divide(new BigDecimal(intervalGoodsList.size()), 2, BigDecimal.ROUND_HALF_DOWN);
        System.err.println("平均价值：" + goodsPriceAvg);

        //判断平均值是否在区间内，不在平均值区间报错
        if (goodsPriceAvg.compareTo(priceAgvMin) <= 0 || goodsPriceAvg.compareTo(priceAgvMax) >= 0) {
            //todo
            throw new GlobalMsgException("合成失败，请重新添加材料合成（区间）");
        }

        //根据概率获取向上向下匹配
        BigDecimal random = BigDecimal.valueOf(Math.random() * (contractRate.getGoodsPriceDownRate().add(contractRate.getGoodsPriceUpRate()).doubleValue()));
        //向下匹配
        List<GuGoods> guGoodsList;
        if (random.compareTo(contractRate.getGoodsPriceDownRate()) <= 0) {
            guGoodsList = goodsList.stream().filter(guGoods -> guGoods.getGoodsPrice().compareTo(totalPriceMin) >= 0 && guGoods.getGoodsPrice().compareTo(totalPriceMax) <= 0).collect(Collectors.toList());
        } else {
            //向上匹配
            guGoodsList = goodsList.stream().filter(guGoods -> guGoods.getGoodsPrice().compareTo(totalPriceMin) >= 0 && guGoods.getGoodsPrice().compareTo(totalPriceMax) <= 0).collect(Collectors.toList());
        }
        //合成材料失效掉
        inventories.forEach(inventory -> inventory.setInventoryState(InventoryConstant.State.CONTRACT_MATERIALS));
        boolean result = this.updateBatchById(inventories);
        if (!result) {
            throw new GlobalMsgException("系统异常，若材料丢失请联系客服");
        }

        //获取最终合成饰品
        GuGoods goods = guGoodsList.get(((int) (Math.random() * guGoodsList.size())));
        GuInventory inventory = this.addInventory(user.getUserId(), user.getUserSteamId(), InventoryConstant.Type.GOODS, InventoryConstant.Source.CONTRACT, null, goods.getGoodsId(), goods.getGoodsPrice());
        goods.setInventoryId(inventory.getInventoryId());

        RedisUtil.deleteKey(RedisConstant.SYNTHETIC_LIST + userId);
        return goods;
    }

    @Override
    public GuInventory bestInventory(Long userId, List<Integer> sources) {
        return inventoryMapper.bestInventory(userId, sources);
    }

    @Override
    public GuGoods bestSynthetic(Long userId) {
        GuInventory inventory = this.bestInventory(userId, Collections.singletonList(InventoryConstant.Source.CONTRACT));
        if (ObjectUtils.isEmpty(inventory)) {
            return null;
        }
        GuGoods goods = goodsService.getOne(new LambdaQueryWrapper<GuGoods>() {
            {
                select(GuGoods::getGoodsId, GuGoods::getGoodsName, GuGoods::getGoodsSameId, GuGoods::getGoodsShortName, GuGoods::getGoodsPrice, GuGoods::getGoodsIconUrl, GuGoods::getGoodsOriginalIconUrl, GuGoods::getGoodsLevel);
                eq(GuGoods::getGoodsId, inventory.getInventoryItemId());
            }
        });
        goods.setInventoryId(inventory.getInventoryId());
        return goods;
    }

    @Override
    public List<GuGoods> getList(long boxId, long size) {
        List<GuGoods> goodsList;
        if (boxId > 0) {
            goodsList = inventoryMapper.listByBoxId(boxId, size);
        } else {
            goodsList = inventoryMapper.listAll(size);
            if (!CollectionUtils.isEmpty(goodsList) && goodsList.size() < size) {
                //不够数量，进行扩容
                for (int i = goodsList.size(); i < size; i++) {
                    goodsList.add(goodsList.get((int) (Math.random() * goodsList.size())));
                }
            }
            goodsList.forEach(goods -> {
                GuBox box = boxService.getBox(goods.getBoxId());
                box.setGoodsIconUrl(goodsList.stream().max(Comparator.comparing(GuGoods::getGoodsPrice)).orElse(new GuGoods()).getGoodsIconUrl());
                goods.setBox(box);
            });
        }

        return goodsList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean exchangeGuCurrency(InventoryBo inventoryBo) {
        GuUser user = userService.getById(inventoryBo.getUserId());
        List<GuInventory> inventories = this.listByIds(inventoryBo.getInventoryIds());
        //校验参数
        if (CollectionUtils.isEmpty(inventories) || inventories.size() != inventoryBo.getInventoryIds().size() || inventories.stream().anyMatch(inventory -> !inventory.getInventoryUserId().equals(inventoryBo.getUserId()))) {
            throw new GlobalMsgException("非法参数");
        }
        if (inventories.stream().anyMatch(inventory -> !inventory.getInventoryType().equals(InventoryConstant.Type.GOODS))) {
            throw new GlobalMsgException("库存类型不正确");
        }
        if (inventories.stream().anyMatch(inventory -> !inventory.getInventoryState().equals(InventoryConstant.State.INVENTORY))) {
            throw new GlobalMsgException("库存状态异常");
        }

        BigDecimal totalPrice = BigDecimal.ZERO;
        //获取兑换咕币的数量
        for (GuInventory inventory : inventories) {
            GuGoods goods = goodsService.getById(inventory.getInventoryItemId());
            totalPrice = totalPrice.add(goods.getGoodsPrice());
            inventory.setInventoryState(InventoryConstant.State.EXCHANGE_GOO_CURRENCY);
        }
        if (this.updateBatchById(inventories)) {
            //增加咕币
            user.setUserGuCurrency(user.getUserGuCurrency().add(totalPrice));
            // 记录日志
//            currencyRecordService.addRecord(user.getUserId(), user.getUserSteamId(), user.getUserNickname(), totalPrice, null, );
            GuCurrencyRecord currencyRecord = new GuCurrencyRecord();
            currencyRecord.setCrUserId(user.getUserId());
            currencyRecord.setCrSteamId(user.getUserSteamId());
            currencyRecord.setCrUserNickname(user.getUserNickname());
            currencyRecord.setCrAmount(totalPrice);
            currencyRecord.setCrOriginalAmount(totalPrice);
            currencyRecord.setCrType(0);
            currencyRecord.setCrSourceType(4);
            currencyRecord.setCrDesc(JSON.toJSONString(inventories));
            currencyRecord.setCrRemark(JSON.toJSONString(inventories));
            return user.updateById();
        }

        return false;
    }

    @Override
    public void sentSynthetic(InventoryBo inventoryBo) {
        if (!CollectionUtils.isEmpty(inventoryBo.getInventoryIds())) {
            List<GuInventory> inventories = this.listByIds(inventoryBo.getInventoryIds());
            //校验参数
            if (CollectionUtils.isEmpty(inventories) || inventories.size() != inventoryBo.getInventoryIds().size() || inventories.stream().anyMatch(inventory -> !inventory.getInventoryUserId().equals(inventoryBo.getUserId()))) {
                throw new GlobalMsgException("非法参数");
            }
            if (inventories.stream().anyMatch(inventory -> !inventory.getInventoryType().equals(InventoryConstant.Type.GOODS))) {
                throw new GlobalMsgException("库存类型不正确");
            }
            if (inventories.stream().anyMatch(inventory -> !inventory.getInventoryState().equals(InventoryConstant.State.INVENTORY))) {
                throw new GlobalMsgException("库存状态异常");
            }
            List<GuGoods> goodsList = new ArrayList<>(10);
            inventories.forEach(inventory -> {
                GuGoods goods = goodsService.getById(inventory.getInventoryItemId());
                goods.setInventoryId(inventory.getInventoryId());
                goodsList.add(goods);
            });
            RedisUtil.setKey(RedisConstant.SYNTHETIC_LIST + inventoryBo.getUserId(), JSON.toJSONString(goodsList));
        }
    }

    @Override
    public List<GuGoods> syntheticList(Long userId) {
        String value = RedisUtil.getValue(RedisConstant.SYNTHETIC_LIST + userId);
        List<GuGoods> goodsList = JSON.parseArray(value, GuGoods.class);
        if (goodsList.size() < 10) {
            int size = goodsList.size();
            for (int i = 0; i < 10 - size; i++) {
                goodsList.add(new GuGoods());
            }
        }
        return goodsList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean trade(GuInventory inventory) {
        //校验库存是否正确
        GuInventory guInventory = this.getById(inventory.getInventoryId());
        if (ObjectUtils.isEmpty(guInventory) || !guInventory.getInventoryUserId().equals(inventory.getInventoryUserId()) || !guInventory.getInventoryType().equals(InventoryConstant.Type.GOODS)) {
            throw new GlobalMsgException("非法操作");
        }
        if (!guInventory.getInventoryState().equals(InventoryConstant.State.INVENTORY)) {
            throw new GlobalMsgException("库存状态异常");
        }
        GuUser user = userService.getById(inventory.getInventoryUserId());
        if (ObjectUtils.isEmpty(user) || ObjectUtils.isEmpty(user.getUserTradingUrl())) {
            throw new GlobalMsgException("请填写交易链接");
        }
        //查询库存
        if (stockService.count(new LambdaQueryWrapper<GuStock>() {
            {
                eq(GuStock::getStockMarketName, guInventory.getInventoryItemName());
            }
        }) <= 0) {
            throw new GlobalMsgException("暂无库存");
        }
        //记录数据 发起交易
        if (withdrawalService.trade(user, guInventory.getInventoryId(), guInventory.getInventoryItemName())) {
            guInventory.setInventoryState(InventoryConstant.State.WITHDRAWAL);
            // 记录日志
            recordService.addRecord(user.getUserId(), RecordConstant.TYPE.USE, InventoryConstant.Source.WITHDRAWAL, inventory.getInventoryId(), InventoryConstant.Type.GOODS, inventory.getInventoryId(), "提取", "");
        }
        return true;
    }

    /*@Override
    public PageVo<GuGoods> backPage(int p, int size, long userId, int state, String searchKey) {
        int index = (p - 1) * size;
        List<GuGoods> pageData = inventoryMapper.getGoodsInventoryPageData(userId, state, index, size);
        long count = inventoryMapper.getGoodsInventoryCount(userId, state);
        return new PageVo<>(pageData, count);
    }*/

    @Override
    public PageVo<GuInventory> backPage(int p, int size, long userId, int state, String searchKey) {
        if (p == 1) {
            p = 0;
        } else {
            p = (p - 1) * size;
        }
        List<GuInventory> guInventories = inventoryMapper.findAll(p, size);
        System.out.println(guInventories.size());

        guInventories.forEach(
                guInventory -> {
                    if (guInventory.getInventoryType() == InventoryConstant.Type.BOX) {
                        GuBox box=boxService.getById(guInventory.getInventoryItemId());
                        guInventory.setItemName(box.getBoxName());
                        guInventory.setImageUrl(box.getBoxImageUrl());
                    } else if (guInventory.getInventoryType() == InventoryConstant.Type.GOODS) {
                        GuGoods goods=goodsService.getById(guInventory.getInventoryItemId());
                        guInventory.setImageUrl(goods.getUserImage());
                        guInventory.setItemName(goods.getGoodsName());
                    } else if (guInventory.getInventoryType() == InventoryConstant.Type.PROP) {
                        GuProp prop = propService.getById(guInventory.getInventoryItemId());
                        guInventory.setItemName(prop.getPropName());
                        guInventory.setImageUrl(prop.getPropImage());
                    }

                }
        );
        long count = inventoryMapper.getGuInventoryCount();
        return new PageVo<>(guInventories, count);
    }
}
